
import os
import socket
import threading
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import qrcode
from PIL import ImageTk, Image

class QRWindow:
    def __init__(self, parent):
        self.parent = parent
        self.window = None
        self.image = None
        
    def show(self, title, ip, port):
        if self.window is None:
            self.window = tk.Toplevel(self.parent)
            self.window.title(title)
            self.window.protocol("WM_DELETE_WINDOW", self.hide)
            
            qr = qrcode.QRCode(version=1, box_size=8, border=4)
            qr.add_data(f"http://{ip}:{port}")
            qr.make(fit=True)
            
            img = qr.make_image(fill_color="black", back_color="white")
            img = img.resize((220, 220), Image.LANCZOS)
            self.image = ImageTk.PhotoImage(img)
            
            frame = tk.Frame(self.window)
            frame.pack(padx=15, pady=15)
            
            tk.Label(frame, image=self.image).pack()
            tk.Label(frame, text=f"IP: {ip}").pack()
            tk.Label(frame, text=f"端口: {port}").pack()
            tk.Button(frame, text="关闭", command=self.hide).pack(pady=5)
        else:
            self.window.deiconify()
    
    def hide(self):
        if self.window:
            self.window.withdraw()

class FileTransfer:
    def __init__(self):
        self.socket = None
        self.active = False
        
    def send_file(self, file_path, port, callback):
        self.active = True
        try:
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.socket.bind(('0.0.0.0', port))
            self.socket.listen(1)
            
            filename = os.path.basename(file_path)
            filesize = os.path.getsize(file_path)
            
            conn, addr = self.socket.accept()
            conn.sendall(f"{filename}|{filesize}".encode())
            
            with open(file_path, 'rb') as f:
                while self.active:
                    data = f.read(4096)
                    if not data: break
                    conn.sendall(data)
                    callback(f.tell(), filesize)
            
            conn.close()
            return True
        except Exception as e:
            messagebox.showerror("错误", f"发送失败: {str(e)}")
            return False
        finally:
            if self.socket: self.socket.close()
            self.active = False

class TransferApp:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("双码快传 v4.2")
        self.transfer = FileTransfer()
        self.send_qr = QRWindow(self.root)
        self.recv_qr = QRWindow(self.root)
        self.setup_ui()
        
    def setup_ui(self):
        main_frame = tk.Frame(self.root, padx=20, pady=20)
        main_frame.pack()
        
        # 发送面板
        send_frame = self.create_panel(main_frame, "发送文件", 0)
        tk.Button(send_frame, text="生成发送码", 
                 command=self.show_send_qr).grid(row=2, column=2, padx=5)
        
        # 接收面板
        recv_frame = self.create_panel(main_frame, "接收文件", 1)
        tk.Button(recv_frame, text="生成接收码",
                 command=self.show_recv_qr).grid(row=2, column=2, padx=5)
        
        self.status = tk.Label(main_frame, text="准备就绪", bd=1, relief=tk.SUNKEN, anchor=tk.W)
        self.status.grid(row=2, column=0, sticky="ew", pady=5)
    
    def create_panel(self, parent, title, row):
        frame = tk.LabelFrame(parent, text=title, padx=10, pady=10)
        frame.grid(row=row, column=0, padx=10, pady=5, sticky="nsew")
        
        # 公共控件
        path_label = tk.Label(frame, text="文件路径:" if row == 0 else "保存路径:")
        path_label.grid(row=0, column=0, sticky="e")
        
        path_entry = tk.Entry(frame, width=35)
        path_entry.grid(row=0, column=1, padx=5)
        
        browse_btn = tk.Button(frame, text="浏览", 
                              command=lambda: self.browse_path(path_entry, row==0))
        browse_btn.grid(row=0, column=2)
        
        ip_label = tk.Label(frame, text="端口:" if row == 0 else "发送方IP:")
        ip_label.grid(row=1, column=0, sticky="e")
        
        if row == 0:
            port_entry = tk.Entry(frame, width=8)
            port_entry.insert(0, "5000")
            port_entry.grid(row=1, column=1, sticky="w", padx=5)
            self.send_port = port_entry
            self.file_entry = path_entry
        else:
            ip_entry = tk.Entry(frame, width=15)
            ip_entry.grid(row=1, column=1, sticky="w", padx=5)
            port_entry = tk.Entry(frame, width=8)
            port_entry.insert(0, "5000")
            port_entry.grid(row=1, column=2, sticky="w")
            self.recv_ip = ip_entry
            self.recv_port = port_entry
            self.save_entry = path_entry
        
        action_btn = tk.Button(frame, text="开始发送" if row == 0 else "开始接收",
                             command=self.start_send if row == 0 else self.start_recv)
        action_btn.grid(row=2, column=1, pady=5)
        
        progress = ttk.Progressbar(frame, orient="horizontal", length=280, mode="determinate")
        progress.grid(row=3, column=0, columnspan=3, pady=5)
        
        if row == 0:
            self.send_progress = progress
            self.send_btn = action_btn
        else:
            self.recv_progress = progress
            self.recv_btn = action_btn
            
        return frame
    
    def browse_path(self, entry, is_file):
        path = filedialog.askopenfilename() if is_file else filedialog.asksaveasfilename()
        if path:
            entry.delete(0, tk.END)
            entry.insert(0, path)
            if is_file:
                self.send_progress["maximum"] = os.path.getsize(path)
    
    def show_send_qr(self):
        ip = socket.gethostbyname(socket.gethostname())
        port = int(self.send_port.get())
        self.send_qr.show("发送二维码", ip, port)
    
    def show_recv_qr(self):
        ip = self.recv_ip.get() or "0.0.0.0"
        port = int(self.recv_port.get())
        self.recv_qr.show("接收二维码", ip, port)
    
    def start_send(self):
        file_path = self.file_entry.get()
        if not file_path:
            messagebox.showerror("错误", "请选择要发送的文件")
            return
            
        port = int(self.send_port.get())
        self.send_btn.config(state="disabled")
        
        threading.Thread(
            target=self.transfer.send_file,
            args=(file_path, port, self.update_send_progress),
            daemon=True
        ).start()
    
    def start_recv(self):
        save_path = self.save_entry.get()
        if not save_path:
            messagebox.showerror("错误", "请选择保存路径")
            return
            
        ip = self.recv_ip.get()
        port = int(self.recv_port.get())
        self.recv_btn.config(state="disabled")
        
        threading.Thread(
            target=self.receive_file,
            args=(ip, port, save_path),
            daemon=True
        ).start()
    
    def receive_file(self, ip, port, save_path):
        try:
            conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            conn.connect((ip, port))
            
            file_info = conn.recv(1024).decode()
            filename, filesize = file_info.split("|")
            filesize = int(filesize)
            
            save_path = os.path.join(os.path.dirname(save_path), filename)
            self.recv_progress["maximum"] = filesize
            received = 0
            
            with open(save_path, 'wb') as f:
                while received < filesize:
                    data = conn.recv(4096)
                    if not data: break
                    f.write(data)
                    received += len(data)
                    self.recv_progress["value"] = received
                    self.status.config(text=f"接收中: {received/1024:.1f}KB/{filesize/1024:.1f}KB")
            
            messagebox.showinfo("完成", f"文件已保存到: {save_path}")
        except Exception as e:
            messagebox.showerror("错误", f"接收失败: {str(e)}")
        finally:
            conn.close()
            self.recv_btn.config(state="normal")
            self.status.config(text="准备就绪")
    
    def update_send_progress(self, current, total):
        self.send_progress["value"] = current
        self.status.config(text=f"发送中: {current/1024:.1f}KB/{total/1024:.1f}KB")
        if current >= total:
            self.send_btn.config(state="normal")
            self.status.config(text="发送完成")
    
    def run(self):
        self.root.mainloop()

if __name__ == "__main__":
    app = TransferApp()
    app.run()
